﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace HerbertDotNet
{
    public class HerbertLevel
    {
        public const int TilesWidth = 25;  // Width of level, in tiles.
        public const int TilesHeight = 25; // Height of level, in tiles.

        public static HerbertLevel FromFile(string fileName)
        {
            // Open file stream.
            using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                return FromStream(stream);
            }
        }

        public static HerbertLevel FromStream(Stream stream)
        {
            HerbertLevel obj = new HerbertLevel();

            using (StreamReader reader = new StreamReader(stream))
            {
                // Read grid of tiles.
                string curRow;
                Tile curTile;

                for (byte y = 0; y < obj.Tiles.GetLength(1); y++)
                {
                    curRow = reader.ReadLine();

                    for (byte x = 0; x < obj.Tiles.GetLength(0); x++)
                    {
                        // Check tile at current position.
                        curTile = Tile.Empty;

                        switch (curRow[x])
                        {
                            case '.':
                                curTile = Tile.Empty;
                                break;
                            case '*':
                                curTile = Tile.GrayButton;
                                break;
                            case 'o':
                                curTile = Tile.WhiteButton;
                                obj.TotalWhiteButtons++;
                                break;
                            case 'O':
                                curTile = Tile.WallLeft | Tile.WallRight | Tile.WallTop | Tile.WallBottom;
                                break;
                            case 'A':
                                curTile = Tile.WallLeft;
                                break;
                            case 'B':
                                curTile = Tile.WallRight;
                                break;
                            case 'C':
                                curTile = Tile.WallTop;
                                break;
                            case 'D':
                                curTile = Tile.WallBottom;
                                break;
                            case 'E':
                                curTile = Tile.WallLeft | Tile.WallRight;
                                break;
                            case 'F':
                                curTile = Tile.WallTop | Tile.WallBottom;
                                break;
                            case 'G':
                                curTile = Tile.WallLeft | Tile.WallTop;
                                break;
                            case 'H':
                                curTile = Tile.WallLeft | Tile.WallBottom;
                                break;
                            case 'I':
                                curTile = Tile.WallRight | Tile.WallTop;
                                break;
                            case 'J':
                                curTile = Tile.WallRight | Tile.WallBottom;
                                break;
                            case 'K':
                                curTile = Tile.WallLeft | Tile.WallRight | Tile.WallTop;
                                break;
                            case 'L':
                                curTile = Tile.WallLeft | Tile.WallRight | Tile.WallBottom;
                                break;
                            case 'M':
                                curTile = Tile.WallLeft | Tile.WallTop | Tile.WallBottom;
                                break;
                            case 'N':
                                curTile = Tile.WallRight | Tile.WallTop | Tile.WallBottom;
                                break;
                            case 'u':
                                obj.StartPosition = new HPoint(x, y);
                                break;
                        }

                        obj.Tiles[x, y] = curTile;
                    }
                }

                // Read attributes of level.
                string curLine;
                string[] curLineParts;
                
                while ((curLine = reader.ReadLine()) != null)
                {
                    curLineParts = curLine.Split(' ');

                    switch(curLineParts[0].ToLower())
                    {
                        case "points":
                            obj.Points = short.Parse(curLineParts[1]);
                            break;
                        case "maxsize":
                            obj.ProgramMaxByteLength = short.Parse(curLineParts[1]);
                            break;
                    }
                }
            }

            return obj;
        }

        public HerbertLevel()
        {
            this.Tiles = new Tile[TilesWidth, TilesHeight];
        }

        public short Points
        {
            get;
            set;
        }

        public int ProgramMaxByteLength
        {
            get;
            set;
        }

        public int TotalWhiteButtons
        {
            get;
            protected set;
        }

        public Tile[,] Tiles
        {
            get;
            set;
        }

        public HPoint StartPosition
        {
            get;
            set;
        }

        [Flags()]
        public enum Tile : byte
        {
            Empty = 0x00,
            GrayButton = 0x01,
            WhiteButton = 0x02,
            WallLeft = 0x04,
            WallRight = 0x08,
            WallTop = 0x10,
            WallBottom = 0x20
        }
    }

    public struct HPoint
    {
        public int X;
        public int Y;

        public static bool operator ==(HPoint a, HPoint b)
        {
            return a.X == b.X && a.Y == b.Y;
        }

        public static bool operator !=(HPoint a, HPoint b)
        {
            return !(a == b);
        }

        public HPoint(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
}
